﻿using System;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Entity.Frames;
using RayDen.Library.Entity.Scene;
using RayDen.RayEngine.Core.Interface;
using RayDen.RayEngine.Core.Types;

namespace RayDen.RayEngine.Data
{
    public class SphereAreaLight : ILight
    {
        public Point Position;
        public RgbSpectrum Power;
        public float FalloffStart, FalloffEnd, Radius = 0.085f;
        public EmissionProfile Profile { get; set; }

        public SampledSpectrum Spectra;
        public string Name
        {
            get { return "SphereAreaLight" + Guid.NewGuid(); }
        }
        public SphereAreaLight() { }

        public SphereAreaLight(FrameLightsource frameLight)
        {
            this.Position = (Point)frameLight.Parameters.Get<Vector>(FrameLightsource.PointLightPosition);
            this.Power = frameLight.Parameters.Get<RgbSpectrum>(FrameLightsource.LightsourcePower);
            this.Spectra = SPD_Data.FromSampled(SPD_Data.D5000, SpectrumType.Illuminant);
            //new SampledSpectrum(ref this.Power, SpectrumType.Illuminant);
        }

        public SphereAreaLight(LightsourceInfo frameLight)
        {
            this.Position = (Point)frameLight.Position;
            this.Power = new RgbSpectrum(frameLight.Spectra);
            this.Spectra = SPD_Data.FromSampled(SPD_Data.D5000, SpectrumType.Illuminant);
            //new SampledSpectrum(ref this.Power, SpectrumType.Illuminant);
        }
        public RgbSpectrumInfo Emittance(ref Vector dir, out float pdf)
        {
            pdf = MC.UniformSpherePdf()*Radius;
            return new RgbSpectrumInfo((Vector)(this.Power / (this.Position * this.Radius - dir).Length2()));
        }
        #region ILightsource Members
        public bool IsDelta
        {
            get
            {
                return true;
            }
        }

        public ISpectrum Le(ref Vector dir)
        {
            return (Power/ (Position*Radius - dir).Length2());
        }
        public void Le(ref Vector dir, ref ISpectrum le)
        {
            var spectrum = Le(ref dir);
            le.Mul(ref spectrum);
        }

        public void EvaluateShadow(ref Point point, ref Normal n, float u0, float u1, float u2, ref LightSample result)
        {
            if (result == null)
                result = new LightSample();

            var l = Position - (point + MC.UniformSampleSphere(u0, u1) * Radius);	// vector to random point on source
            var Dist = l.Length;		// distance to light source


            result.Distance = Dist;
            result.Pdf = MC.UniformSpherePdf();
            result.LightRay = new RayData(point, l/Dist, 1e-4f, Dist - 1e-4f);
//            result.Spectrum = GlobalConfiguration.Instance.SpectralRendering ? (ISpectrum) (Spectra / Dist) : (Power / Dist);

            float theta = Vector.SphericalTheta(ref result.LightRay.Dir);

            var i = Profile.Evaluate(Vector.SphericalPhi(ref result.LightRay.Dir) * MathLab.INVTWOPI, theta * MathLab.INVPI);
            i.Div(Dist);
            var p = (ISpectrum)Power;
            i.Mul(ref p);
            result.Spectrum = i;
        }

        public void EvaluatePhoton(IRayEngineScene scene, float u0, float u1, float u2, float u3, float u4, out LightSample result)
        {
            result = new LightSample();

            var dir = MC.UniformSampleSphere(u0, u1);

            result.LightRay = new RayData(ref Position, ref dir, 1e-4f, 1e4f);

            result.Pdf = MathLab.INV4PI/Radius;
            float theta = Vector.SphericalTheta(ref result.LightRay.Dir);
            var i = (RgbSpectrumInfo)Profile.Evaluate(Vector.SphericalPhi(ref result.LightRay.Dir) * MathLab.INVTWOPI, theta * MathLab.INVPI);

            result.Spectrum = i * Radius * MathLab.M_PI * 4f;
        }



        #endregion





        public void SampleSpatialDirect(ref Point point, ref Normal n, float u0, float u1, float u2, ref LightSample ls)
        {
            var l = Position - (point + MC.UniformSampleSphere(u0, u1) * Radius);	// vector to random point on source
            ls.Distance = l.Length;		// distance to light source

            ls.Pdf = MC.UniformSpherePdf();
            ls.LightRay.Dir = l / ls.Distance;

        }

        public void SampleSpatialAdjoint(IRayEngineScene scene, float u0, float u1, float u2, float u3, float u4, out Vector dir, out float distance, out float pdf)
        {
            dir = MC.UniformSampleSphere(u0, u1);
            distance = Radius;
            pdf = MathLab.INV4PI;
        }
    }
}